#pragma once
#include <map>

////////////////////////////////////////////////////////////////////////////////////
/// @brief Represents a block of memory.
///
/// An instance of this class represents a memory pool which we can query to
/// allocate memory. The benefit with this is that the memory is not fragmented
/// which will increase the performance in the application. Though allocation will
/// function slower.
////////////////////////////////////////////////////////////////////////////////////
class MemoryPool 
{
public:
	////////////////////////////////////////////////////////////////////////////////////
	/// @brief Exception class
	////////////////////////////////////////////////////////////////////////////////////
	class Error : public std::exception {};

	////////////////////////////////////////////////////////////////////////////////////
	/// @brief Exception class for when an invalid pointer is given.
	////////////////////////////////////////////////////////////////////////////////////
	class InvalidPointerError : public Error {};

	////////////////////////////////////////////////////////////////////////////////////
	/// @brief Exception class for when an allocation fails
	////////////////////////////////////////////////////////////////////////////////////
	class AllocationError : public Error {};

	////////////////////////////////////////////////////////////////////////////////////
	/// @brief Exception class for when this pool don't have enough free memory.
	////////////////////////////////////////////////////////////////////////////////////
	class InsufficentMemoryError : public AllocationError {};

	////////////////////////////////////////////////////////////////////////////////////
	/// @brief Constructor for the memory pool.
	///
	/// @param size The size in bytes for this pool.
	////////////////////////////////////////////////////////////////////////////////////
	MemoryPool(size_t size = 1048576);

	////////////////////////////////////////////////////////////////////////////////////
	/// @brief The destructor.
	////////////////////////////////////////////////////////////////////////////////////
	~MemoryPool();

	////////////////////////////////////////////////////////////////////////////////////
	/// @brief Acquire and reserve some memory from the pool.
	///
	/// @param size Amount of memory in bytes that you want.
	/// @return Pointer to the memory.
	////////////////////////////////////////////////////////////////////////////////////
	void *GetMemory(size_t size);

	////////////////////////////////////////////////////////////////////////////////////
	/// @brief Free memory that was previously reservered with GetMemory.
	///
	/// @param memory The memory to free.
	////////////////////////////////////////////////////////////////////////////////////
	void FreeMemory(void *memory);

	////////////////////////////////////////////////////////////////////////////////////
	/// @brief Getter function for the size of this pool.
	///
	/// @return The size of this pool.
	////////////////////////////////////////////////////////////////////////////////////
	size_t GetSize() const;

	////////////////////////////////////////////////////////////////////////////////////
	/// @brief Will check the pool if there is room for the size specified.
	///
	/// It is not recommended that you use this one as it does the same work as 
	/// the GetMemory function except that it won't allocate any memory. So if the
	/// pool is very large with many small allocated objects then this process can be
	/// very demanding to do twice!
	/// @param size The size we want to check for.
	/// @return True if this size can be allocated.
	////////////////////////////////////////////////////////////////////////////////////
	bool CanAllocate(size_t size) const;

protected:
	////////////////////////////////////////////////////////////////////////////////////
	/// @brief Copy constructor.
	////////////////////////////////////////////////////////////////////////////////////
	MemoryPool(const MemoryPool &copy);

	////////////////////////////////////////////////////////////////////////////////////
	/// @brief Find a big enough free place in the pool memory.
	///
	/// @param size The size that needs to be free in bytes.
	/// @return Pointer to the free memory or NULL.
	////////////////////////////////////////////////////////////////////////////////////
	void *FindFreeMemory(size_t size) const;

private:
	////////////////////////////////////////////////////////////////////////////////////
	/// @brief Pointer to this memory pools real memory.
	////////////////////////////////////////////////////////////////////////////////////
	void * myMemory;

	////////////////////////////////////////////////////////////////////////////////////
	/// @brief The size of the memory pool
	////////////////////////////////////////////////////////////////////////////////////
	size_t mySize;

	////////////////////////////////////////////////////////////////////////////////////
	/// @brief Collection with all allocated memory in our pool.
	////////////////////////////////////////////////////////////////////////////////////
	std::map<void *, size_t> myPointers;
};
